package cn.tedu.v6;

import java.io.*;
import java.net.BindException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.*;

/**
 * Version 6在线用户列表
 */


public class Server {
    private ServerSocket serverSocket;
    private static final Integer PORT=4545;
    private List<PrintWriter> allOut=new ArrayList<>();
  //1.私有化集合保存所有用户名--onlineUsers
  private List<String> onlineUsers = new ArrayList<>();
    private Map<String,String>userDateBase;
    public Server() {
        try {
            serverSocket = new ServerSocket(PORT);
            userDateBase=new HashMap<>();
            userDateBase.put("海燕","123456");
            userDateBase.put("小宝","654321");

        } catch (BindException e) {
            System.out.println("端口号重复申请，建议换个端口号");
        } catch (IOException e) {
            System.out.println("申请端口号失败~~~");
        }
    }
    public void start() {
        try {
            while (true) {
                System.out.println("----等待用户端链接---");
                Socket socket = serverSocket.accept();
                new Thread(new ClientHandler(socket)).start();
            }
        } catch (IOException e) {
            System.out.println("服务器端通讯断开了");
        } finally {
            if (serverSocket!=null&&!serverSocket.isClosed()){
                try {
                    serverSocket.close();
                } catch (IOException e) {
                    System.out.println("服务器关闭失败");
                }
            }
        }
    }
    public static void main(String[] args)  {
        new Server().start();
    }
    private synchronized void broadcastMessage(String message){
        for(PrintWriter pw:allOut){
            pw.println(message);
        }
    }
    //2.封装方法：同步转发给所有客户端消息：“在线用户：”+username
    //updateOnlineUsers 更新在线人数的方法：“在线用户：‘+username
    //字符串：String userList ="在线用户”+String.join(",",onlineUsers)
    private synchronized void updateOnlineUsers() {
        synchronized (onlineUsers) {
            String userList = "在线用户：" + String.join("、", onlineUsers);
            broadcastMessage("用户名消息" + userList);
        }
    }
        private class ClientHandler implements Runnable {
            private Socket socket;
            private String ip;
            private BufferedReader br;
            private PrintWriter pw;
            private String username;
            private String password;

            public ClientHandler(Socket s) {
                this.socket = s;
                ip = socket.getInetAddress().getHostAddress();
                try {
                    br = new BufferedReader(new InputStreamReader(socket.getInputStream(), "UTF8"));
                    pw = new PrintWriter(new BufferedWriter(new OutputStreamWriter(socket.getOutputStream(), "UTF8")), true);

                    synchronized (allOut) {
                        allOut.add(pw);
                    }
                } catch (IOException e) {
                    System.out.println("客户端断开通讯");
                }
            }

            private synchronized void broadcastMessage(String message) {
                for (PrintWriter pw : allOut) {
                    pw.println("消息广播：" + message);
                }
            }

            private boolean Login(String user, String pass) {
                return userDateBase.containsKey(user) && userDateBase.get(user).equals(pass);
            }

            private void handlerChat() {
                new Thread(() -> {
                    try {
                        String message;
                        while ((message = br.readLine()) != null) {
                            System.out.println("客户端说：" + message);
                            System.out.println("服务器端请输入：");
                            updateOnlineUsers();
                            if ("exit".equalsIgnoreCase(message)) {
                                pw.println("exit");
                                System.out.println(ip + "客户端退出聊天室~");
                                break;
                            }

                            broadcastMessage(message);
                        }
                    } catch (IOException e) {
                        System.out.println("客户端读入失败了");
                    }
                }).start();
                Scanner input = new Scanner(System.in);
                while (true) {
                    System.out.println("服务器请输入：");
                    String say = input.nextLine();
                    pw.println("服务器说：" + say);
                    if ("exit".equalsIgnoreCase(say)) {
                        System.out.println("服务器端退出聊天室~");
                        break;
                    }

                }
            }

            public void run() {

                try {

                    pw.println("请输入用户名");
                    username = br.readLine();
                    pw.println("请输入密码");
                    password = br.readLine();
                    if (Login(username, password)) {
                        pw.println("登录成功，欢迎进入聊天室");
                        //3.所有用户添加至集合中【同步块：登录集合】
                        // 3.1转发在线用户方法
                        synchronized (onlineUsers) {
                            onlineUsers.add(username);
                            System.out.println(username + "连接成功");
                            updateOnlineUsers();
                        }
                        handlerChat();
                    } else {

                        pw.println("登录失败，用户名或者密码错误");
                        socket.close();
                    }


                } catch (NullPointerException e) {
                    System.out.println("IO流传入对象，有对象为null,立马核对，建议：查看try块中对象可能赋值为null");
                } catch (IOException e) {
                    System.out.println("服务器断开链接");
                } finally {
                    //4.客户端断开链接，移除当前集合【同步】
                    synchronized (allOut) {
                        allOut.remove(pw);
                    }
                    synchronized (onlineUsers) {
                        onlineUsers.remove(username);
                    }
                    if (br != null)
                        try {
                            br.close();
                        } catch (IOException e) {
                            System.out.println("结束通讯：关闭失败！");
                        }
                    if (pw != null) pw.close();
                    if (socket != null && !socket.isClosed())
                        try {
                            socket.close();
                        } catch (IOException e) {
                            System.out.println("通讯关闭失败~");
                        }
                }
            }
        }
    }

